跳到主要内容

SpringEL

什么是 SpringEL

参考文档 Spring 表达式语言 (SpEL) 参考资料 第5部分:表达式语言SpEL(这个讲的比较详细,如果想要尝试脱离 Spring 执行就看这个) 参考资料 spring boot SpEL 参考资料 spring-boot周边游(二)SpEL

Spring Expression Language (SpEL) 是强大的表达式语言,支持查询、操作运行时对象图,以及解析逻辑、算术表达式。划重点:SpEL 可以独立使用

Spring 在 Spring3 就中引入 SpringELSpEL 是一种强大简洁的装配 Bean 的方式,他可以通过运行期间执行的表达式将值装配到我们的属性或构造函数当中,更可以调用 JDK 中提供的静态常量,获取外部 Properties 文件中的的配置

SpEL 拥有很多特性,包括:

  • 使用 Bean 的 ID 来引用 Bean
  • 调用方法和访问对象的属性
  • 对值进行算术、关系和逻辑运算
  • 正则表达式匹配
  • 集合操作

这篇文章主要介绍一下怎么使用,具体细节看官网

依赖包

<!-- 一般无需添加这个依赖,因为 Spring3 之后的都自带了 -->
<!-- 虽然可以单独提出来使用,但是一般都是结合着使用。所以这个只做了解 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>

使用 Spring 的表达接口求值

如果不使用 Spring 而是单独拿出来使用的话就是如下的操作(就是解释器模式)

// 下面的代码使用 SpEL API来解析文本字符串表达式 Hello World.
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue(); // "hello world"

exp = parser.parseExpression("'Hello World'.concat('!')");
message = (String) exp.getValue();// "Hello World!"

基本语法

注意:在 @Value 使用 SpEL 是采用 # 开头(#{expression}),如果要读取配置文件里的属性使用的是 $ 开头(${properties}),但是也支持嵌套读取 如 #{${someProperty} + 2}

@Value("#{ }")                                   //标记会提示 Spring 这个标记里的内容是 SpEL 表达式。
@Value("#{rootBean.nestBean.property}") // "." 操作符表示属性或方法引用,支持层次调用
@Value("#{aList[0] }") // 数组和列表使用方括号获得内容
@Value("#{aMap[key] }") // maps 使用方括号获得内容
@Value("#{rootBean?.property}") // 此处 "?" 是安全导航运算符器,避免空指针异常
@Value("#{condition ? trueValue : falseValue}") // 三元运算符(IF-THEN-ELSE)
@Value("#{valueA?:defaultValue}") // Elvis 操作符,当 valueA 为空时赋值 defaultValue

使用 SpEL 装配 Bean

Spring 支持通过运行期执行的表达式将值装配到 Bean 的属性或构造器参数中。Spring boot 默认使用基于注解的配置,@Value 注解可以在域,方法和方法/构造器参数中使用来指定一个默认值。

@Value("#{xxxx}")
private String attribute1;

文字表达式

@Value("#{24}")          //整型
@Value("#{'中国'}") //String
//里面有个单引号,如果去掉会报错。String 类型的字面值可以使用单引号或双引号作为字符串的界定符。
@Value("my name is #{'david'}") //与非SpEL 表达式的值混用
@Value("#{120.4}") //浮点型数字
@Value("#{true}") //布尔值

引用 Bean 属性和方法

@Value("#{beanId}")                     //使用Bean ID 将一个Bean 装配到另一个Bean 的属性中
@Value("#{beanId.attribute}") //使用Bean 的引用来获取Bean 的属性
@Value("#{beanId.getSomenthing()}") //调用引用Bean的方法

类表达式

SpEL 使用 T() 运算符调用类作用域的方法和常量。

@Value("#{T(java.lang.Math).PI}") 
@Value("#{T(java.lang.Math).random()}")
@Value("#{T(java.lang.Math).random() * 100.0 }")

算数运算符

@Value("#{19 + 1}") // add的值为20
private double add;

@Value("#{'String1 ' + 'string2'}") //addString的值为"String1 string2"
private String addString;

@Value("#{20 - 1}") // subtract值为19
private double subtract;

@Value("#{10 * 2}") // multiply值为20
private double multiply;

@Value("#{36 / 2}") // divide值为19
private double divide;

@Value("#{36 div 2}") // div操作等价与/
private double divideAlphabetic;

@Value("#{37 % 10}") // modulo值为7
private double modulo;

@Value("#{37 mod 10}") // mod操作等价于%
private double moduloAlphabetic;

@Value("#{2 ^ 9}") // powerOf的值为512
private double powerOf;

@Value("#{(2 + 2) * 2 + 9}") // brackets值为17
private double brackets;

关联和逻辑运算符

@Value("#{1 == 1}") // equal值为true
private boolean equal;

@Value("#{1 eq 1}") // eq等价于==
private boolean equalAlphabetic;

@Value("#{1 != 1}") // notEqual值为false
private boolean notEqual;

@Value("#{1 ne 1}") // ne等价于!=
private boolean notEqualAlphabetic;

@Value("#{1 < 1}") // lessThan的值为false
private boolean lessThan;

@Value("#{1 lt 1}") // lt等价于<
private boolean lessThanAlphabetic;

@Value("#{1 <= 1}") // lessThanOrEqual的值为true
private boolean lessThanOrEqual;

@Value("#{1 le 1}") // le等价于<=
private boolean lessThanOrEqualAlphabetic;

@Value("#{1 > 1}") // greaterThan的值为false
private boolean greaterThan;

@Value("#{1 gt 1}") // gt等价于>
private boolean greaterThanAlphabetic;

@Value("#{1 >= 1}") // greaterThanOrEqual的值为true
private boolean greaterThanOrEqual;

@Value("#{1 ge 1}") // ge等价于>=
private boolean greaterThanOrEqualAlphabetic;

@Value("#{250 > 200 && 200 < 4000}") // and值为true
private boolean and;

@Value("#{250 > 200 and 200 < 4000}") // and等价于&&
private boolean andAlphabetic;

@Value("#{400 > 300 || 150 < 100}") // or的值为true
private boolean or;

@Value("#{400 > 300 or 150 < 100}") // or等价于||
private boolean orAlphabetic;

@Value("#{!true}") // not值为false
private boolean not;

@Value("#{not true}") // not等价于!
private boolean notAlphabetic;

条件运算符

@Value("#{2 > 1 ? 'a' : 'b'}") // ternary的值为b
private String ternary;

@Value("#{someBean.someProperty != null ? someBean.someProperty : 'default'}")
private String ternary;

@Value("#{someBean.someProperty ?: 'default'}") // 等价于上式的执行结果
private String elvis;

正则表达式

使用 matches 语句来判断当前的字符串是否满足特定的正则表达式

@Value("#{'100' matches '\\d+' }") // Will inject true
private boolean validNumericStringResult;

@Value("#{'100fghdjf' matches '\\d+' }") // Will inject false
private boolean invalidNumericStringResult;

@Value("#{'valid alphabetic string' matches '[a-zA-Z\\s]+' }") // Will inject true
private boolean validAlphabeticStringResult;

@Value("#{'invalid alphabetic string #$1' matches '[a-zA-Z\\s]+' }") // Will inject false
private boolean invalidAlphabeticStringResult;

@Value("#{someBean.someValue matches '\d+'}") // Will inject true if someValue contains only digits
private boolean validNumericValue;

获取 List 和 Map 数据类型

假设现在有一个 Bean 名称为 workersHolder,这个组件中有两个 fields,一个是 workers 的链表,这里记录了所有的员工名称,还有一个是 salaryByWorkers 键值对 map,里面存储了员工姓名和其对应的薪水。现在这个 bean 中初始化了四个员工信息,如何能将这四个员工的信息读取出来?

@Component("workersHolder")
public class WorkersHolder {
private List<String> workers = new LinkedList<>();
private Map<String, Integer> salaryByWorkers = new HashMap<>();

public WorkersHolder() {
workers.add("John");
workers.add("Susie");
workers.add("Alex");
workers.add("George");

salaryByWorkers.put("John", 35000);
salaryByWorkers.put("Susie", 47000);
salaryByWorkers.put("Alex", 12000);
salaryByWorkers.put("George", 14000);
}

//Getters and setters
}

map 采用 componentName.mapName['key'] 读取 key 对应的 value list 采用 componentName.listName[index] 读取对应 index 下标的元素

@Value("#{workersHolder.salaryByWorkers['John']}") // Will inject 35000
private Integer johnSalary;

@Value("#{workersHolder.salaryByWorkers['George']}") // Will inject 14000
private Integer georgeSalary;

@Value("#{workersHolder.salaryByWorkers['Susie']}") // Will inject 47000
private Integer susieSalary;

@Value("#{workersHolder.workers[0]}") // Will inject John
private String firstWorker;

@Value("#{workersHolder.workers[3]}") // Will inject George
private String lastWorker;

@Value("#{workersHolder.workers.size()}") // Will inject 4
private Integer numberOfWorkers;